#ifndef POINT3D_H
#define POINT3D_H

#define DOT(A,B)		(A.x*B.x+A.y*B.y+A.z*B.z)
#define NORMALIZE(A)	{float l=1/sqrtf(A.x*A.x+A.y*A.y+A.z*A.z);A.x*=l;A.y*=l;A.z*=l;}
#define LENGTH(A)		(sqrtf(A.x*A.x+A.y*A.y+A.z*A.z))
#define SQRLENGTH(A)	(A.x*A.x+A.y*A.y+A.z*A.z)
#define SQRDISTANCE(A,B) ((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)+(A.z-B.z)*(A.z-B.z))

#include <math.h>
#include <algorithm>
#include <iostream>
#include <sstream>


/**
*
* improve performance with : http://www.flipcode.com/archives/Faster_Vector_Math_Using_Templates.shtml
*
*
*/
using std::min;
using std::max;

class Point3D
{
public:
	double x, y, z;
	Point3D(void)
	{
		x = 0;
		y = 0;
		z = 0;
	}

	Point3D(double coor)
	{
		x = coor;
		y = coor;
		z = coor;
	}

	Point3D(double xx, double yy, double zz) : x(xx), y(yy), z(zz) {}
	Point3D& normalize()
	{
		double nor2 = length2();
		if (nor2 > 0) {
			double invNor = 1 / sqrt(nor2);
			x *= invNor, y *= invNor, z *= invNor;
		}
		return *this;
	}
	inline Point3D operator * (double &f) { return Point3D(x * f, y * f, z * f); }
	inline Point3D operator * (Point3D &v) { return Point3D(x * v.x, y * v.y, z * v.z); }
	inline double dot(Point3D &v) { return x * v.x + y * v.y + z * v.z; }
	inline Point3D operator - (Point3D &v) { return Point3D(x - v.x, y - v.y, z - v.z); }
	inline Point3D operator + (Point3D &v) { return Point3D(x + v.x, y + v.y, z + v.z); }
	inline Point3D& operator *= (Point3D &v) { x *= v.x, y *= v.y, z *= v.z; return *this; }
	inline Point3D operator - () { return Point3D(-x, -y, -z); }
	inline double length2()  { return x * x + y * y + z * z; }
	inline double length() { return sqrt(length2()); }
	inline double operator [] (int& p_index)
	{
		switch (p_index)
		{
		case 0:
			return x;
		case 1:
			return y;
		case 2:
			return z;
		default:
			return 0;
		}
	}
	/*inline Point3D operator - (Normal& normal)
	{
		return Point3D(x - normal.x, y - normal.y, z - normal.z);
	}*/


	void Min(Point3D& currentPoint)
	{
		x = min(x, currentPoint.x);
		y = min(y, currentPoint.y);
		z = min(z, currentPoint.z);
	}

	void Max(Point3D& currentPoint)
	{
		x = max(x, currentPoint.x);
		y = max(y, currentPoint.y);
		z = max(z, currentPoint.z);
	}

	inline Point3D operator/ (const double & c)
	{
		return Point3D(x / c, y / c, z / c);
	}

	inline Point3D operator/ (const double & c) const
	{
		return Point3D(x / c, y / c, z / c);
	}

	inline bool operator != (const int& number)
	{
		if (x == number && y == number && z == number)
			return false;
		else
			return true;
	}

	Point3D CrossProduct(Point3D& rhs)
	{
		//#define CROSS(rez,v1,v2) \
		//	rez.x = v1.y*v2.z - v1.z*v2.y; \
		//	rez.y = v1.z*v2.x - v1.x*v2.z; \
		//	rez.z = v1.x*v2.y - v1.y*v2.x;

		float xx = y * rhs.z - z * rhs.y;
		float yy = z * rhs.x - x * rhs.z;
		float zz = x * rhs.y - y * rhs.x;

		return Point3D(xx, yy, zz);
	}

	void Normalize()
	{
		float temp = 1 / length();

		x = x * temp;
		y = y * temp;
		z = z * temp;
	}

	inline bool operator == (Point3D& p_point)
	{
		return x == p_point.x && y == p_point.y && z == p_point.z;
	}

	std::string toString()
	{
		std::stringstream  test;
		test << (double)x << "," << (double)y << "," << (double)z;
		return test.str();
	}

	//Point3D(const Point3D &currentPoint)
	//{
	//	x = currentPoint.x;
	//	y = currentPoint.y;
	//	z = currentPoint.z;
	//}

	//Point3D(const double xx, const double yy, const double zz) : x(xx), y(yy), z(zz)
	//{}

	//Point3D operator = (const Point3D & rhs)
	//{
	//	x = rhs.x; y = rhs.y; z = rhs.z;
	//	return (*this);
	//}

	//inline Point3D operator- (void) const
	//{
	//	return (Point3D(-x, -y, -z));
	//}

	//inline Point3D operator - (const Point3D& p)
	//{
	//	Point3D returnPoint = Point3D(x - p.x, y - p.y, z - p.z);
	//	return returnPoint;
	//}

	//inline Point3D operator - (const Point3D& p) const
	//{
	//	return Point3D(x - p.x, y - p.y, z - p.z);
	//}

	//inline Point3D operator * (const double& c)
	//{
	//	return Point3D(x * c, y * c, z * c);
	//}

	//inline Point3D operator * (const double& c) const
	//{
	//	return Point3D(x * c, y * c, z * c);
	//}

	///*Point3D operator * (const Point3D& point)
	//{
	//	return 
	//}*/

	//inline Point3D operator + (const Point3D& p)
	//{
	//	return Point3D(x + p.x, y + p.y , z + p.z);
	//}

	//inline Point3D operator + (const Point3D& p) const
	//{
	//	return Point3D(x + p.x, y + p.y, z + p.z);
	//}

	//inline Point3D operator/ (const double & c)
	//{
	//	return Point3D(x / c, y / c, z / c);
	//}

	//inline Point3D operator/ (const double & c) const
	//{
	//	return Point3D(x / c, y / c, z / c);
	//}

	//inline bool operator != (const int& number)
	//{
	//	if (x == number && y == number && z == number)
	//		return false;
	//	else
	//		return true;
	//}

	//float Maginitude()
	//{
	//	return sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2));
	//}

	//void Normalize()
	//{
	//	float temp = 1 / Maginitude();

	//	x = x * temp;
	//	y = y * temp;
	//	z = z * temp;
	//}

	//Point3D CrossProduct(Point3D& rhs)
	//{
	//	float xx = y * rhs.z - rhs.y * z;
	//	float yy = x * rhs.z - rhs.x * z;
	//	float zz = x * rhs.y - rhs.x * y;

	//	return Point3D(xx, yy, zz);
	//}



	

};

inline Point3D operator * (const double& lhs, const Point3D& p)
{
	return Point3D(p.x * lhs, p.y * lhs, p.z * lhs);
}

inline Point3D operator+ (const double& lhs, const Point3D& rhs)
{
	return Point3D(rhs.x + lhs, rhs.y + lhs, rhs.z + lhs);
}

#endif // !Point3D_H
